home *** CD-ROM | disk | FTP | other *** search
/ Aminet 24 / Aminet 24 (1998)(GTI - Schatztruhe)[!][Apr 1998].iso / Aminet / comm / mail / Mutt089src.lha / Mutt-0.89i-AMIGA / src / mh.c < prev    next >
C/C++ Source or Header  |  1998-01-28  |  12KB  |  531 lines

  1. /*
  2.  * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
  3.  * 
  4.  *     This program is free software; you can redistribute it and/or modify
  5.  *     it under the terms of the GNU General Public License as published by
  6.  *     the Free Software Foundation; either version 2 of the License, or
  7.  *     (at your option) any later version.
  8.  * 
  9.  *     This program is distributed in the hope that it will be useful,
  10.  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  *     GNU General Public License for more details.
  13.  * 
  14.  *     You should have received a copy of the GNU General Public License
  15.  *     along with this program; if not, write to the Free Software
  16.  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  */ 
  18.  
  19. /*
  20.  * This file contains routines specific to MH and ``maildir'' style mailboxes
  21.  */
  22.  
  23. #include "mutt.h"
  24. #include "mx.h"
  25. #include "mailbox.h"
  26. #include "parse.h"
  27.  
  28. #include <sys/stat.h>
  29. #include <dirent.h>
  30. #include <limits.h>
  31. #include <unistd.h>
  32. #include <ctype.h>
  33. #include <errno.h>
  34. #include <string.h>
  35.  
  36. void mh_parse_message (CONTEXT *ctx,
  37.                const char *subdir,
  38.                const char *fname,
  39.                int *count,
  40.                int isOld)
  41. {
  42.   char path[_POSIX_PATH_MAX];
  43.   char *p;
  44.   FILE *f;
  45.   HEADER *h;
  46.   struct stat st;
  47.  
  48.   if (subdir)
  49.     snprintf (path, sizeof (path), "%s/%s/%s", ctx->path, subdir, fname);
  50.   else
  51.     snprintf (path, sizeof (path), "%s/%s", ctx->path, fname);
  52.  
  53.   if ((f = fopen (path, "r")) != NULL)
  54.   {
  55.     (*count)++;
  56.  
  57.     if (!ctx->quiet && ReadInc && ((*count % ReadInc) == 0 || *count == 1))
  58.       mutt_message ("Reading %s... %d", ctx->path, *count);
  59.  
  60.     if (ctx->msgcount == ctx->hdrmax)
  61.       mx_alloc_memory (ctx);
  62.  
  63.     h = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
  64.  
  65.     if (subdir)
  66.     {
  67.       snprintf (path, sizeof (path), "%s/%s", subdir, fname);
  68.       h->path = safe_strdup (path);
  69.     }
  70.     else
  71.       h->path = safe_strdup (fname);
  72.  
  73.     h->env = mutt_read_rfc822_header (f, h);
  74.  
  75.     fstat (fileno (f), &st);
  76.     fclose (f);
  77.  
  78.     if (!h->received)
  79.       h->received = h->date_sent;
  80.  
  81.     if (h->content->length <= 0)
  82.       h->content->length = st.st_size - h->content->offset;
  83.  
  84.     /* index doesn't have a whole lot of meaning for MH and maildir mailboxes,
  85.      * but this is used to find the current message after a resort in 
  86.      * the `index' event loop.
  87.      */
  88.     h->index = ctx->msgcount;
  89.  
  90.     if (ctx->magic == M_MAILDIR)
  91.     {
  92.       /* maildir stores its flags in the filename, so ignore the flags in
  93.        * the header of the message
  94.        */
  95.  
  96.       h->old = isOld;
  97.  
  98. #ifdef AMIGA
  99. /* ':' is volume separator and cannot be used in filenames */
  100. /* we're using ';' to be consistent with qmail port */
  101.       if ((p = strchr (h->path, ';')) != NULL && strncmp (p + 1, "2,", 2) == 0)
  102. #else
  103.       if ((p = strchr (h->path, ':')) != NULL && strncmp (p + 1, "2,", 2) == 0)
  104. #endif /* AMIGA */
  105.       {
  106.     p += 3;
  107.     while (*p)
  108.     {
  109.       switch (*p)
  110.       {
  111.         case 'F':
  112.  
  113.           h->flagged = 1;
  114.           break;
  115.  
  116.         case 'S': /* seen */
  117.  
  118.           h->read = 1;
  119.           break;
  120.  
  121.         case 'R': /* replied */
  122.  
  123.           h->replied = 1;
  124.           break;
  125.       }
  126.       p++;
  127.     }
  128.       }
  129.     }
  130.  
  131.     /* set flags and update context info */
  132.     mx_update_context (ctx);
  133.   }
  134. }
  135.  
  136. /* Ignore the garbage files.  A valid MH message consists of only
  137.  * digits.  Deleted message get moved to a filename with a comma before
  138.  * it.
  139.  */
  140. int mh_valid_message (const char *s)
  141. {
  142.   for (; *s ; s++)
  143.   {
  144.     if (!isdigit (*s))
  145.       return 0;
  146.   }
  147.   return 1;
  148. }
  149.  
  150. /* Read a MH/maildir style mailbox.
  151.  *
  152.  * args:
  153.  *    ctx [IN/OUT]    context for this mailbox
  154.  *    subdir [IN]    NULL for MH mailboxes, otherwise the subdir of the
  155.  *            maildir mailbox to read from
  156.  */
  157. int mh_read_dir (CONTEXT *ctx, const char *subdir)
  158. {
  159.   DIR *dirp;
  160.   struct dirent *de;
  161.   char buf[_POSIX_PATH_MAX];
  162.   int isOld = 0;
  163.   int count = 0;
  164.   struct stat st;
  165.   
  166.   if (subdir)
  167.   {
  168.     snprintf (buf, sizeof (buf), "%s/%s", ctx->path, subdir);
  169.     isOld = (strcmp ("cur", subdir) == 0) && option (OPTMARKOLD);
  170.   }
  171.   else
  172.     strfcpy (buf, ctx->path, sizeof (buf));
  173.  
  174.   if (stat (buf, &st) == -1)
  175.     return (-1);
  176.  
  177.   if ((dirp = opendir (buf)) == NULL)
  178.     return (-1);
  179.  
  180.   if (!subdir || (subdir && strcmp (subdir, "new") == 0))
  181.     ctx->mtime = st.st_mtime;
  182.  
  183.   while ((de = readdir (dirp)) != NULL)
  184.   {
  185.     if (ctx->magic == M_MH)
  186.     {
  187.       if (!mh_valid_message (de->d_name))
  188.     continue;
  189.     }
  190.     else if (*de->d_name == '.')
  191.     {
  192.       /* Skip files that begin with a dot.  This currently isn't documented
  193.        * anywhere, but it was a suggestion from the author of QMail on the
  194.        * mailing list.
  195.        */
  196.       continue;
  197.     }
  198.  
  199.     mh_parse_message (ctx, subdir, de->d_name, &count, isOld);
  200.   }
  201.  
  202.   closedir (dirp);
  203.   return 0;
  204. }
  205.  
  206. /* read a maildir style mailbox */
  207. int maildir_read_dir (CONTEXT *ctx)
  208. {
  209.   /* maildir looks sort of like MH, except that there are two subdirectories
  210.    * of the main folder path from which to read messages
  211.    */
  212.   if (mh_read_dir (ctx, "new") == -1 || mh_read_dir (ctx, "cur") == -1)
  213.     return (-1);
  214.  
  215.   return 0;
  216. }
  217.  
  218. /* Open a new (unique) message in a maildir mailbox.  In order to avoid the
  219.  * need for locks, the filename is generated in such a way that it is unique,
  220.  * even over NFS: <time>.<pid>_<count>.<hostname>.  The _<count> part is
  221.  * optional, but required for programs like Mutt which do not change PID for
  222.  * each message that is created in the mailbox (otherwise you could end up
  223.  * creating only a single file per second).
  224.  */
  225. void maildir_create_filename (const char *path, HEADER *hdr, char *msg, char *full)
  226. {
  227.   char subdir[_POSIX_PATH_MAX];
  228.   char suffix[16];
  229.   struct stat sb;
  230.  
  231.   /* the maildir format stores the status flags in the filename */
  232.   suffix[0] = 0;
  233.  
  234.   if (hdr && (hdr->flagged || hdr->replied || hdr->read))
  235.   {
  236. #ifdef AMIGA
  237.     sprintf (suffix, ";2,%s%s%s",
  238. #else
  239.     sprintf (suffix, ":2,%s%s%s",
  240. #endif /* AMIGA */
  241.          hdr->flagged ? "F" : "",
  242.          hdr->replied ? "R" : "",
  243.          hdr->read ? "S" : "");
  244.   }
  245.  
  246.   if (hdr && (hdr->read || hdr->old))
  247.     strfcpy (subdir, "cur", sizeof (subdir));
  248.   else
  249.     strfcpy (subdir, "new", sizeof (subdir));
  250.  
  251.   FOREVER
  252.   {
  253.     snprintf (msg, _POSIX_PATH_MAX, "%s/%ld.%d_%d.%s%s",
  254.           subdir, time (NULL), getpid (), Counter++, Hostname, suffix);
  255.     snprintf (full, _POSIX_PATH_MAX, "%s/%s", path, msg);
  256.     if (stat (full, &sb) == -1 && errno == ENOENT) return;
  257.   }
  258. }
  259.  
  260. static int maildir_sync_message (CONTEXT *ctx, int msgno)
  261. {
  262.   HEADER *h = ctx->hdrs[msgno];
  263.   char newpath[_POSIX_PATH_MAX];
  264.   char fullpath[_POSIX_PATH_MAX];
  265.   char oldpath[_POSIX_PATH_MAX];
  266.   char *p;
  267.  
  268.   /* decide which subdir this message belongs in */
  269.   strfcpy (newpath, (h->read || h->old) ? "cur" : "new", sizeof (newpath));
  270.   strcat (newpath, "/");
  271.  
  272.   if ((p = strchr (h->path, '/')) == NULL)
  273.   {
  274.     dprint (1, (debugfile, "maildir_sync_message: %s: unable to find subdir!\n",
  275.         h->path));
  276.     return (-1);
  277.   }
  278.   p++;
  279.   strcat (newpath, p);
  280.  
  281.   /* kill the previous flags */
  282. #ifdef AMIGA
  283. /* beaware of the dreaded ':' */
  284.   if ((p = strchr (newpath, ';')) != NULL) *p = 0;
  285. #else
  286.   if ((p = strchr (newpath, ':')) != NULL) *p = 0;
  287. #endif /* AMIGA */
  288.  
  289.   if (h->replied || h->read || h->flagged)
  290.   {
  291. #ifdef AMIGA
  292.     strcat (newpath, ";2,");
  293. #else
  294.     strcat (newpath, ":2,");
  295. #endif /* AMIGA */
  296.     if (h->flagged) strcat (newpath, "F");
  297.     if (h->replied) strcat (newpath, "R");
  298.     if (h->read) strcat (newpath, "S");
  299.   }
  300.  
  301.   snprintf (fullpath, sizeof (fullpath), "%s/%s", ctx->path, newpath);
  302.   snprintf (oldpath, sizeof (oldpath), "%s/%s", ctx->path, h->path);
  303.  
  304.   if (strcmp (fullpath, oldpath) == 0)
  305.   {
  306.     /* message hasn't really changed */
  307.     return 0;
  308.   }
  309.  
  310.   if (rename (oldpath, fullpath) != 0)
  311.   {
  312.     mutt_perror ("rename");
  313.     return (-1);
  314.   }
  315.   safe_free ((void **)&h->path);
  316.   h->path = safe_strdup (newpath);
  317.   return (0);
  318. }
  319.  
  320. /* save changes to a message to disk */
  321. static int mh_sync_message (CONTEXT *ctx, int msgno)
  322. {
  323.   HEADER *h = ctx->hdrs[msgno];
  324.   FILE *f;
  325.   FILE *d;
  326.   MESSAGE *msg;
  327.   int rc = -1;
  328.   char oldpath[_POSIX_PATH_MAX];
  329.   char newpath[_POSIX_PATH_MAX];
  330.   long loc = 0;
  331.   int chflags = CH_